using System;
using ParserGenerator.Extensions;

namespace ParserGenerator.CharacterMapping
{
    [Serializable]
    public struct CharInterval : IEquatable<CharInterval>, IComparable<CharInterval>, IComparable
    {
        public static readonly CharInterval Universal = new CharInterval(CharExtensions.MinChar, CharExtensions.MaxChar);

        public int First { get; }
        public int Last { get; }
        public int Length => Last - First + 1;

        public CharInterval(int c)
        {
            if (c < CharExtensions.MinChar || c > CharExtensions.MaxChar)
                throw new ArgumentOutOfRangeException(nameof(c), SR.InvalidUnicodeCodepoint);
            First = Last = c;
        }

        public CharInterval(int first, int last)
        {
            if (first < CharExtensions.MinChar || first > CharExtensions.MaxChar)
                throw new ArgumentOutOfRangeException(nameof(first), SR.InvalidUnicodeCodepoint);
            if (last < CharExtensions.MinChar || last > CharExtensions.MaxChar)
                throw new ArgumentOutOfRangeException(nameof(last), SR.InvalidUnicodeCodepoint);
            if (first > last)
                throw new ArgumentException(SR.FirstLastNotRange);
            First = first;
            Last = last;
        }

        public static CharInterval New(int c)
        {
            if (c < CharExtensions.MinChar || c > CharExtensions.MaxChar)
                throw new ArgumentOutOfRangeException(nameof(c), SR.InvalidUnicodeCodepoint);
            return new CharInterval(c);
        }

        public static CharInterval New(int first, int last)
        {
            if (first < CharExtensions.MinChar || first > CharExtensions.MaxChar)
                throw new ArgumentOutOfRangeException(nameof(first), SR.InvalidUnicodeCodepoint);
            if (last < CharExtensions.MinChar || last > CharExtensions.MaxChar)
                throw new ArgumentOutOfRangeException(nameof(last), SR.InvalidUnicodeCodepoint);
            if (first > last)
                throw new ArgumentException(SR.FirstLastNotRange);
            return new CharInterval(first, last);
        }

        public override int GetHashCode()
        {
            return ICollectionExtensions.CombineHashCodes(First, Last);
        }

        public bool Equals(CharInterval other)
        {
            return First == other.First && Last == other.Last;
        }

        public override bool Equals(object obj)
        {
            return obj is CharInterval && Equals((CharInterval)obj);
        }

        public int CompareTo(CharInterval other)
        {
            int t = First.CompareTo(other.First);
            if (t != 0)
                return t;
            return Last.CompareTo(other.Last);
        }

        public int CompareTo(object obj)
        {
            if (obj == null)
                return 1;
            else if (obj is CharInterval)
                return CompareTo((CharInterval)obj);
            else
                throw new ArgumentException(SR.ArgumentMustBeCharInterval, nameof(obj));
        }

        public bool Includes(CharInterval other)
        {
            return First <= other.First && other.Last <= Last;
        }

        public bool Contains(int c)
        {
            return First <= c && c <= Last;
        }

        private static string Format(int c)
        {
            return c.AsSingleQuote();
        }

        public override string ToString()
        {
            return Format(First) + " ... " + Format(Last);
        }
    }
}
